package com.example.demo.order.security;

import com.example.demo.order.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import jakarta.annotation.Resource;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName RateLimitUtil
 * @Description Redis版限流工具类
 * @Author MingChang.Wei
 * @Date 2025/9/26
 **/
@Slf4j
@Component("rateLimitUtil")
public class RateLimitUtil {
    
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final String RATE_LIMIT_PREFIX = "rate_limit:";
    
    // Lua脚本：实现滑动窗口限流
    private static final String RATE_LIMIT_SCRIPT = 
        "local key = KEYS[1]\n" +
        "local window = tonumber(ARGV[1])\n" +
        "local limit = tonumber(ARGV[2])\n" +
        "local current = tonumber(ARGV[3])\n" +
        "\n" +
        "-- 移除过期的记录\n" +
        "redis.call('zremrangebyscore', key, 0, current - window)\n" +
        "\n" +
        "-- 获取当前窗口内的请求数量\n" +
        "local currentRequests = redis.call('zcard', key)\n" +
        "\n" +
        "if currentRequests < limit then\n" +
        "    -- 添加当前请求\n" +
        "    redis.call('zadd', key, current, current)\n" +
        "    redis.call('expire', key, window)\n" +
        "    return 1\n" +
        "else\n" +
        "    return 0\n" +
        "end";
    
    /**
     * 检查是否超过限流阈值
     */
    private boolean checkRateLimit(String key, int maxCount, int windowSeconds) {
        try {
            DefaultRedisScript<Long> script = new DefaultRedisScript<>();
            script.setScriptText(RATE_LIMIT_SCRIPT);
            script.setResultType(Long.class);
            
            long currentTime = System.currentTimeMillis();
            long windowMs = windowSeconds * 1000L;
            
            Long result = redisTemplate.execute(script, 
                Collections.singletonList(key), 
                windowMs, maxCount, currentTime);
            
            return result != null && result == 1L;
        } catch (Exception e) {
            log.error("限流检查失败, key={}", key, e);
            // 发生异常时，为了系统稳定性，允许通过
            return true;
        }
    }
    
    /**
     * 检查用户操作频率限流
     */
    public void checkRateLimit(Long userId, String operation, int maxCount, int timeWindow) {
        String key = RATE_LIMIT_PREFIX + operation + ":" + userId;
        
        if (!checkRateLimit(key, maxCount, timeWindow)) {
            log.warn("用户操作频率超限: userId={}, operation={}, limit={}/{}", 
                    userId, operation, maxCount, timeWindow);
            throw new BusinessException("操作过于频繁，请稍后再试");
        }
    }
    
    /**
     * 购物车操作限流 - 每分钟最多100次
     */
    public void checkCartOperationLimit(Long userId) {
        checkRateLimit(userId, "cart_operation", 100, 60);
    }
    
    /**
     * 订单创建限流 - 每分钟最多10次
     */
    public void checkOrderCreateLimit(Long userId) {
        checkRateLimit(userId, "order_create", 10, 60);
    }
    
    /**
     * 支付操作限流 - 每分钟最多5次
     */
    public void checkPaymentLimit(Long userId) {
        checkRateLimit(userId, "payment", 5, 60);
    }
    
    /**
     * 简单的计数器限流（用于不需要滑动窗口的场景）
     */
    public boolean simpleRateLimit(String key, int maxCount, int expireSeconds) {
        try {
            String rateLimitKey = RATE_LIMIT_PREFIX + "simple:" + key;
            
            Long currentCount = redisTemplate.opsForValue().increment(rateLimitKey);
            
            if (currentCount == 1) {
                // 第一次访问，设置过期时间
                redisTemplate.expire(rateLimitKey, expireSeconds, TimeUnit.SECONDS);
            }
            
            return currentCount <= maxCount;
        } catch (Exception e) {
            log.error("简单限流检查失败, key={}", key, e);
            return true; // 异常时允许通过
        }
    }
}
